#pragma once

//----------------------------------------------------------------------------
// NOTE:
//
// The CDynamicList class was written by Arthur Liberman and is used in several
// of his other projects, both for his studies and his own public projects.
//----------------------------------------------------------------------------

#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

/////////////////////////////////////////////////
///   Written by Arthur Liberman              ///
///                                           ///
///   CDynamicList : Template List Class      ///
///   class CDynamicNode<TYPE> : item class   ///
///   class CDynamicList<TYPE> : list class   ///
/////////////////////////////////////////////////

template <class T>
class CDynamicList;

///////////////////////////////////////////
///   class CDynamicNode: declaration   ///
///////////////////////////////////////////

template <class T>
class CDynamicNode
{
	friend class CDynamicList<T>;

public:
	CDynamicNode(T *pItem, bool bDelete);
	virtual ~CDynamicNode(void);

	CDynamicNode<T> *GetNextNode();

	T *GetValue();

protected:
	void SetNextNode(CDynamicNode<T> *pNextNode);

	T *m_pItem;
	CDynamicNode<T> *m_pNextNode;
	bool m_bDelete;

private:
	CDynamicNode(void);
};

//////////////////////////////////////////////
///   class CDynamicNode: implementation   ///
//////////////////////////////////////////////

template <class T>
CDynamicNode<T> *CDynamicNode<T>::GetNextNode() { return m_pNextNode; }

template <class T>
T *CDynamicNode<T>::GetValue() { return m_pItem; }

template <class T>
void CDynamicNode<T>::SetNextNode(CDynamicNode<T> *pNextNode) { m_pNextNode = pNextNode; }

template <class T>
CDynamicNode<T>::CDynamicNode() {}

template <class T>
CDynamicNode<T>::CDynamicNode(T *_pItem, bool _bDelete)
{
	m_pItem = _pItem;
	m_bDelete = _bDelete;
	m_pNextNode = NULL; 
}

template <class T>
CDynamicNode<T>::~CDynamicNode(void)
{
	if (m_bDelete)
		delete m_pItem;
	m_pItem = NULL;
}

///////////////////////////////////////////
///   class CDynamicList: declaration   ///
///////////////////////////////////////////

template <class T>
class CDynamicList
{
public:
	CDynamicList(void);
	virtual ~CDynamicList(void);

	int ListCount();
	int AddItem(T *pNewItem, bool bAutoFreeMem = true);
	int AddItemRef(T &pNewItem, bool bAutoFreeMem = true);
	int AddItemToFirst(T *pNewItem, bool bAutoFreeMem = true);
	bool RemoveItem(CDynamicNode<T> *pItem);
	bool RemoveItem(T *pValue);
	bool RemoveCurrentItem();
	void ClearList();
	
	CDynamicNode<T> *SelectFirst();
	CDynamicNode<T> *SelectLast();
	CDynamicNode<T> *SelectNext();
	CDynamicNode<T> *GetCurrent();
	T *GetValue();
	T &GetValueRef();
	int ToArray(T* &typeList);
	
	CDynamicList<T>& operator =  (const CDynamicList<T>& list);
	CDynamicList<T>& operator += (T *pNewItem);
	CDynamicList<T>& operator -= (CDynamicNode<T> *pItem);
	CDynamicList<T>& operator -= (T *pValue);
	T& operator[] (int index);
protected:
	int m_iItemCount;

	CDynamicNode<T> *m_pFirst;
	CDynamicNode<T> *m_pLast;
	CDynamicNode<T> *m_pCurrent;

	bool RemoveItemDirect(CDynamicNode<T> *pItem, CDynamicNode<T> *pPrevItem);
};

//////////////////////////////////////////////
///   class CDynamicList: implementation   ///
//////////////////////////////////////////////

template <class T>
int CDynamicList<T>::ListCount() { return m_iItemCount; }

template <class T>
CDynamicList<T>::CDynamicList(void)
{
	m_iItemCount = 0;
	m_pFirst = NULL;
	m_pLast = NULL;
	m_pCurrent = NULL;
}

template <class T>
CDynamicList<T>::~CDynamicList(void)
{
	ClearList();
}

template <class T>
int CDynamicList<T>::AddItem(T *_pNewItem, bool _bDelete)
{
	CDynamicNode<T> *Item = NULL;

	if (!_pNewItem)
		return 0;

	Item = new CDynamicNode<T>(_pNewItem, _bDelete);
	if (!m_pFirst)
		m_pFirst = Item;

	if (!m_pLast)
		m_pLast = Item;
	else
	{
		m_pLast->SetNextNode(Item);
		m_pLast = Item;
	}

	return ++m_iItemCount;
}

template <class T>
int CDynamicList<T>::AddItemRef(T &_pNewItem, bool _bDelete)
{
	return this->AddItem(&_pNewItem, _bDelete);
}

template <class T>
int CDynamicList<T>::AddItemToFirst(T *_pNewItem, bool _bDelete)
{
	CDynamicNode<T> *Item = NULL;

	if (!_pNewItem)
		return 0;

	Item = new CDynamicNode<T>(_pNewItem, _bDelete);
	Item->SetNextNode(m_pFirst);

	if (m_pFirst == m_pCurrent)
		m_pCurrent = Item;

	m_pFirst = Item;

	if (!m_pLast)
		m_pLast = Item;

	return ++m_iItemCount;
}

template <class T>
bool CDynamicList<T>::RemoveItemDirect(CDynamicNode<T> *_pItem, CDynamicNode<T> *_pPrevItem)
{
	bool bRet = false;

	if (_pItem)
	{
		if (_pItem == m_pFirst && _pItem == m_pLast)
		{
			m_pFirst = NULL;
			m_pLast = NULL;
		}
		else if (_pItem == m_pFirst)
			m_pFirst = _pItem->GetNextNode();
		else if (_pItem == m_pLast)
		{
			m_pLast = _pPrevItem;
			m_pLast->SetNextNode(NULL);
		}
		else
			_pPrevItem->SetNextNode(_pItem->GetNextNode());
		
		if (_pItem == m_pCurrent)
			m_pCurrent = _pItem->GetNextNode();

		delete _pItem;
		m_iItemCount--;
		bRet = true;
	}

	return bRet;
}

template <class T>
bool CDynamicList<T>::RemoveItem(CDynamicNode<T> *_pItem)
{
	bool bRet = false;
	CDynamicNode<T> *pNode = NULL, *pPrevNode = NULL;

	if (!_pItem)
		return bRet;

	pNode = m_pFirst;
	while (pNode)
	{
		if (pNode == _pItem)
		{
			bRet = RemoveItemDirect(pNode, pPrevNode);
			break;
		}
		pPrevNode = pNode;
		pNode = pNode->GetNextNode();
	}

	return bRet;
}

template <class T>
bool CDynamicList<T>::RemoveItem(T *_pValue)
{
	bool bRet = false;
	CDynamicNode<T> *pNode = NULL, *pPrevNode = NULL;

	if (!_pValue)
		return bRet;

	pNode = m_pFirst;
	while (pNode)
	{
		if (pNode->GetValue() == _pValue)
		{
			bRet = RemoveItemDirect(pNode, pPrevNode);
			break;
		}
		pPrevNode = pNode;
		pNode = pNode->GetNextNode();
	}

	return bRet;
}

template <class T>
bool CDynamicList<T>::RemoveCurrentItem()
{
	return RemoveItem(m_pCurrent);
}

template <class T>
void CDynamicList<T>::ClearList()
{
	CDynamicNode<T> *Item, *ItemNext = NULL;

	Item = SelectFirst();

	while (Item)
	{
		ItemNext = Item->GetNextNode();
		delete Item;
		Item = ItemNext;
	}

	m_pFirst = NULL;
	m_pLast = NULL;
	m_pCurrent = NULL;
	m_iItemCount = 0;
}

template <class T>
CDynamicNode<T> *CDynamicList<T>::SelectFirst()
{
	m_pCurrent = m_pFirst;
	return m_pCurrent;
}

template <class T>
CDynamicNode<T> *CDynamicList<T>::SelectLast()
{
	m_pCurrent = m_pLast;
	return m_pCurrent;
}

template <class T>
CDynamicNode<T> *CDynamicList<T>::SelectNext()
{
	if (m_pCurrent)
		m_pCurrent = m_pCurrent->GetNextNode();

	return m_pCurrent;
}

template <class T>
CDynamicNode<T> *CDynamicList<T>::GetCurrent()
{
	return m_pCurrent;
}

template <class T>
T *CDynamicList<T>::GetValue()
{
	if (m_pCurrent)
		return m_pCurrent->GetValue();
	else
		return NULL;
}

template <class T>
T &CDynamicList<T>::GetValueRef()
{
	if (m_pCurrent)
		return *(m_pCurrent->GetValue());
	else
		throw (TEXT("DynamicList is out of bounds"));
}

template <class T>
int CDynamicList<T>::ToArray(T* &_typeList)
{
	CDynamicNode<T> *pCurPosition = m_pCurrent;
	_typeList = NULL;
	int i = 0;

	if (m_iItemCount)
	{
		_typeList = new T[m_iItemCount];
		SelectFirst();
		while (GetCurrent())
		{
			_typeList[i] = *GetValue();
			SelectNext();
			i++;
		}
	}

	m_pCurrent = pCurPosition;
	
	return m_iItemCount;
}

template <class T>
CDynamicList<T>& CDynamicList<T>::operator =(const CDynamicList<T>& list)
{
	if (this != &list)
	{
		this->ClearList();
		for (CDynamicNode<T> *node = list.m_pFirst; node; node = node->GetNextNode())
			this->AddItem(new T(*node->GetValue()));
	}

	return *this;
}

template <class T>
CDynamicList<T>& CDynamicList<T>::operator +=(T *_pNewItem)
{
	AddItem(_pNewItem);
	return *this;
}

template <class T>
CDynamicList<T>& CDynamicList<T>::operator -=(CDynamicNode<T> *_pItem)
{
	RemoveItem(_pItem);
	return *this;
}

template <class T>
CDynamicList<T>& CDynamicList<T>::operator -=(T *_pValue)
{
	RemoveItem(_pValue);
	return *this;
}

template <class T>
T& CDynamicList<T>::operator[] (int _index)
{	
	CDynamicNode<T> *pCurPosition = m_pFirst;
	for (int i = 0; i < _index && pCurPosition; i++)
		pCurPosition = pCurPosition->GetNextNode();

	if (!pCurPosition)
		throw (TEXT("DynamicList is out of bounds"));
	else
		return *(pCurPosition->GetValue());
}